package com.dwd.hrm.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.dwd.hrm.constant.CourseConstant;
import com.dwd.hrm.domain.CourseType;
import com.dwd.hrm.domain.CrumbsVo;
import com.dwd.hrm.mapper.CourseTypeMapper;
import com.dwd.hrm.query.CourseTypeQuery;
import com.dwd.hrm.service.ICourseTypeService;
import com.dwd.hrm.util.PageList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <p>
 * 课程目录 服务实现类
 * </p>
 *
 * @author wendi
 * @since 2021-03-05
 * <p>
 * 双端检索涉及到可见性问题
 * 数据库缓存双写一致性
 */
@Service
public class CourseTypeServiceImpl extends ServiceImpl<CourseTypeMapper, CourseType> implements ICourseTypeService {
    @Autowired
    private CourseTypeMapper courseTypeMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    private Lock lock = new ReentrantLock();

    @Override
    public List<CourseType> queryTree() {
        //sql查询
        //return courseTypeMapper.queryTree();
        //从redis查询是否有缓存，没有区数据库查询，然后放入redis
        Object obj = redisTemplate.opsForValue().get(CourseConstant.COURSE_TYPE_CACHE);
        //有缓存
        if (obj != null) {
            System.out.println();
            List<CourseType> courseTypeList = (List<CourseType>) obj;
            return courseTypeList;
        } else {
            List<CourseType> list = new ArrayList<>();
            //双端检索  加锁解决击穿
            if (obj == null) {
                lock.lock();
                try {
                    if (obj == null) {
                        //数据库查询
                        list = queryTreeMethod();
                        //放入redis 设置随机时间， 解决雪崩
                        redisTemplate.opsForValue().set(
                                CourseConstant.COURSE_TYPE_CACHE,
                                list,
                                new Random().nextInt(10000),
                                TimeUnit.MINUTES);
                    }
                } finally {
                    lock.unlock();
                }
            }
            return list;
        }
    }

    /**
     * 重写分页
     *
     * @param query
     * @return
     */
    @Override
    public PageList<CourseType> queryPage(CourseTypeQuery query) {
        Page<CourseType> page = new Page<CourseType>(query.getPage(), query.getRows());
        List<CourseType> lists = courseTypeMapper.queryPage(page, query);
        return new PageList<>(page.getTotal(), lists);
    }

    /**
     * 获取面包屑
     *
     * @param courseTypeId
     * @return
     */
    @Override
    public List<CrumbsVo> getCrumbs(Long courseTypeId) {
        //用来封装
        List<CrumbsVo> crumbsVos = new ArrayList<>();
        CourseType courseType = courseTypeMapper.selectById(courseTypeId);
        //获取连续结点
        String path = courseType.getPath();
        //分割得到所有的id
        String[] split = path.split("\\.");
        for (String s : split) {
            //封装每一个vo
            CrumbsVo crumbsVo = new CrumbsVo();
            //得到每一个类型
            CourseType courseType1 = courseTypeMapper.selectById(Long.valueOf(s));
            //设置自己
            crumbsVo.setOwnerProductType(courseType1);
            //得到pid，用来查询同级
            Long pid = courseType1.getPid();
            //查询所有pid相等的所有结点
            List<CourseType> bro = courseTypeMapper.selectList(new EntityWrapper<CourseType>().eq("pid", pid));
            //排除自己
            if (bro != null) {
                Iterator<CourseType> iterator = bro.iterator();
                while (iterator.hasNext()) {
                    //得到每一个
                    CourseType next = iterator.next();
                    if (next.getId().equals(courseType1.getId())) {
                        //排除自己
                        iterator.remove();
                        //退出
                        break;
                    }
                }
            }
            //设置同级
            crumbsVo.setOtherProductTypes(bro);
            //每一个添加到集合
            crumbsVos.add(crumbsVo);
        }

        return crumbsVos;
    }

    private List<CourseType> queryTreeMethod() {
        //返回结果
        List<CourseType> result = new ArrayList<>();
        //查询所有
        List<CourseType> courseTypes = courseTypeMapper.selectList(null);
        //遍历 进行id和对象的一对一对应
        Map<Long, CourseType> typeHashMap = new HashMap<>(16);
        courseTypes.forEach(courseType -> {
            typeHashMap.put(courseType.getId(), courseType);
        });
        //遍历把子节点放入父对象的children中
        courseTypes.forEach(courseType -> {
            //判断是否有子节点
            if (courseType.getPid().intValue() == 0) {//没有子节点
                //加入返回结果
                result.add(courseType);
            } else {//有子节点
                //获取父节点对象
                CourseType type = typeHashMap.get(courseType.getPid());
                //添加子节点
                type.getChildren().add(courseType);
            }
        });
        return result;
    }

    /**
     * 增删改之前都删除缓存
     * 延迟双删
     *
     * @param entity
     * @return
     */
    @Override
    public boolean insert(CourseType entity) {
        //删除
        redisTemplate.delete(CourseConstant.COURSE_TYPE_CACHE);
        try {
            TimeUnit.MILLISECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        boolean insert = super.insert(entity);
        //丢进rabbitmq  然后一直发，可以设置重试次数  最后不行手动删除
        redisTemplate.delete(CourseConstant.COURSE_TYPE_CACHE);
        return insert;
    }

    @Override
    public boolean deleteById(Serializable id) {
        redisTemplate.delete(CourseConstant.COURSE_TYPE_CACHE);
        try {
            TimeUnit.MILLISECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        boolean deleteById = super.deleteById(id);
        redisTemplate.delete(CourseConstant.COURSE_TYPE_CACHE);
        return deleteById;
    }

    @Override
    public boolean update(CourseType entity, Wrapper<CourseType> wrapper) {
        redisTemplate.delete(CourseConstant.COURSE_TYPE_CACHE);
        try {
            TimeUnit.MILLISECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        boolean update = super.update(entity, wrapper);
        redisTemplate.delete(CourseConstant.COURSE_TYPE_CACHE);
        return update;
    }
}
